/*
* PhysicsMgr.cpp
* 
* Created February 3rd, 2012
*
*/

#pragma region INCLUDEs
#include "PhysicsMgr.h"
#include "MathHelper.h"


#include <iostream>
using namespace std;
#pragma endregion



namespace Physics
{
	///Re-Declare static variable for Compiler Pre-Processing
	PhysicsMgr* PhysicsMgr::mgr = NULL;
	//list<Shapes_Ptr> PhysicsMgr::shapes;
	list<Box2D> PhysicsMgr::box2D;
	list<Box3D> PhysicsMgr::box3D;
	list<Circle2D> PhysicsMgr::circle2D;
	list<Circle3D> PhysicsMgr::circle3D;

	int PhysicsMgr::assignObjID = 0;


#pragma region INSTANTIATION
	//C002: Create Single Instance
	PhysicsMgr* PhysicsMgr::Instance()
	{
		//Lazy Instantiation
		if ( mgr == NULL )
		{
			mgr = new PhysicsMgr();
			//shapes = new list<Shapes_Ptr>();
		}
		//
		return mgr;
	}

	//C003: Clear the array stored with shapes
	PhysicsMgr::~PhysicsMgr()
	{
		//Remove all contents from array and delete array
		Reset();
		delete mgr;  mgr = 0;
	}

	//C004: Destructor
	void PhysicsMgr::Reset()
	{
		//shapes.clear();
		box2D.clear();
		box3D.clear();
		circle2D.clear();
		circle3D.clear();
	}
#pragma endregion


#pragma region ADD_OBJECT
	//M003: Add a Box2D to Physics Array
	void PhysicsMgr::CreateObject( const Box2D &box )
	{
		box2D.push_back(box);

		//assign unique objID
		++assignObjID;
		box2D.back().setID( assignObjID );
	}

	//M004: Add a Box3D to Physics Array
	void PhysicsMgr::CreateObject( const Box3D &box )
	{
		box3D.push_back(box);

		//assign unique objID
		box3D.back().setID( ++assignObjID );
	}

	//M005: Add a Circle2D to Physics Array
	void PhysicsMgr::CreateObject( const Circle2D &circle )
	{
		circle2D.push_back(circle);

		//assign unique objID
		circle2D.back().setID( ++assignObjID );
	}

	//M006: Add a Circle3D to Physics Array
	void PhysicsMgr::CreateObject( const Circle3D &circle )
	{
		circle3D.push_back(circle);

		//assign unique objID
		circle3D.back().setID( ++assignObjID );
	}
#pragma endregion 


#pragma region UPDATE_OBJECTs

	void PhysicsMgr::update_Box2D( const double time )
	{
		list<Box2D>::iterator i;
		for ( i = box2D.begin(); i != box2D.end(); ++i )
		{
			Vector2 tempPos;

			tempPos.setX( time * (*i).getDirection().getX() * (*i).getSpeed() );
			tempPos.setY( time * (*i).getDirection().getY() * (*i).getSpeed() );

			//add the new movement vector2 to position of shape
			(*i).translatePosition(tempPos);
		}
	}

	void PhysicsMgr::update_Box3D( const double time )
	{
		list<Box3D>::iterator i;
		for ( i = box3D.begin(); i != box3D.end(); ++i )
		{
			Vector3 tempPos;

			tempPos.setX( time * (*i).getDirection().getX() * (*i).getSpeed() );
			tempPos.setY( time * (*i).getDirection().getY() * (*i).getSpeed() );
			tempPos.setZ( time * (*i).getDirection().getZ() * (*i).getSpeed() );

			//add the new movement vector3 to position of shape
			(*i).translatePosition(tempPos);
		}
	}

	void PhysicsMgr::update_Circle2D( const double time )
	{
		list<Circle2D>::iterator i;
		for ( i = circle2D.begin(); i != circle2D.end(); ++i )
		{
			Vector2 tempPos;

			tempPos.setX( time * (*i).getDirection().getX() * (*i).getSpeed() );
			tempPos.setY( time * (*i).getDirection().getY() * (*i).getSpeed() );
			
			//add the new movement vector2 to position of shape
			(*i).translatePosition(tempPos);
		}
	}

	void PhysicsMgr::update_Circle3D( const double time )
	{
		list<Circle3D>::iterator i;
		for ( i = circle3D.begin(); i != circle3D.end(); ++i )
		{
			Vector3 tempPos;

			tempPos.setX( time * (*i).getDirection().getX() * (*i).getSpeed() );
			tempPos.setY( time * (*i).getDirection().getY() * (*i).getSpeed() );
			tempPos.setZ( time * (*i).getDirection().getZ() * (*i).getSpeed() );

			//add the new movement vector3 to position of shape
			(*i).translatePosition(tempPos);
		}
	}

	//M006: Update all physics transformation
	void PhysicsMgr::PhysicsUpdate( const double time )
	{
		//Update all objects
		update_Box2D( time );
		update_Box3D( time );
		update_Circle2D( time );
		update_Circle3D( time );
	}
#pragma endregion 


#pragma region REMOVE_OBJECT_BY_COMPARE

	//M007: Remove a Box2D to Physics Array
	bool PhysicsMgr::RemoveObject( const Box2D &box )
	{
		bool removed = false;
		list<Box2D>::iterator it;

		for( it = box2D.begin(); it != box2D.end(); ++it )
		{
			//Compare objID, if there is a match, REMOVE shape
			if ( (*it).getID() == box.getID() ) 
			{
				box2D.erase(it); //remove shape
				removed = true;
				break;
			}
		}
		//
		return removed;
	}

	//M008: Remove a Box3D to Physics Array
	bool PhysicsMgr::RemoveObject( const Box3D &box )
	{
		bool removed = false;
		list<Box3D>::iterator it;

		for( it = box3D.begin(); it != box3D.end(); ++it )
		{
			//Compare objID, if there is a match, REMOVE shape
			if ( (*it).getID() == box.getID() )
			{
				box3D.erase(it); //remove shape
				removed = true;
				break;
			}
		}
		//
		return removed;
	}

	//M009: Remove a Circle2D to Physics Array
	bool PhysicsMgr::RemoveObject( const Circle2D &circle )
	{
		bool removed = false;
		list<Circle2D>::iterator it;

		for( it = circle2D.begin(); it != circle2D.end(); ++it )
		{
			//Compare objID, if there is a match, REMOVE shape
			if ( (*it).getID() == circle.getID() )
			{
				circle2D.erase(it); //remove shape
				removed = true;
				break;
			}
		}
		//
		return removed;
	}

	//M010: Remove a Circle3D to Physics Array
	bool PhysicsMgr::RemoveObject( const Circle3D &circle )
	{
		bool removed = false;
		list<Circle3D>::iterator it;

		for( it = circle3D.begin(); it != circle3D.end(); ++it )
		{
			//Compare objID, if there is a match, REMOVE shape
			if ( (*it).getID() == circle.getID() )
			{
				circle3D.erase(it); //remove shape
				removed = true;
				break;
			}
		}
		//
		return removed;
	}
#pragma endregion 


#pragma region REMOVE_First-Last_Item
//M011: Remove Last obj in Shape array
	bool PhysicsMgr::RemoveLast_Box2D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( box2D.size() > ZERO )
		{
			box2D.pop_back();
			removed = true;
		}
		//
		return removed;
	}

	//M012: Remove First obj in Shape array
	bool PhysicsMgr::RemoveFirst_Box2D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( box2D.size() > ZERO )
		{
			box2D.pop_front();
			removed = true;
		}
		//
		return removed;
	}

	//M011: Remove Last obj in Shape array
	bool PhysicsMgr::RemoveLast_Box3D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( box3D.size() > ZERO )
		{
			box3D.pop_back();
			removed = true;
		}
		//
		return removed;
	}

	//M012: Remove First obj in Shape array
	bool PhysicsMgr::RemoveFirst_Box3D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( box3D.size() > ZERO )
		{
			box3D.pop_front();
			removed = true;
		}
		//
		return removed;
	}


	//M011: Remove Last obj in Shape array
	bool PhysicsMgr::RemoveLast_Circle2D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( circle2D.size() > ZERO )
		{
			circle2D.pop_back();
			removed = true;
		}
		//
		return removed;
	}

	//M012: Remove First obj in Shape array
	bool PhysicsMgr::RemoveFirst_Circle2D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( circle2D.size() > ZERO )
		{
			circle2D.pop_front();
			removed = true;
		}
		//
		return removed;
	}

	//M011: Remove Last obj in Shape array
	bool PhysicsMgr::RemoveLast_Circle3D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( circle3D.size() > ZERO )
		{
			circle3D.pop_back();
			removed = true;
		}
		//
		return removed;
	}

	//M012: Remove First obj in Shape array
	bool PhysicsMgr::RemoveFirst_Circle3D()
	{
		//Implementation code goes here..
		bool removed = false;

		if ( circle3D.size() > ZERO )
		{
			circle3D.pop_front();
			removed = true;
		}
		//
		return removed;
	}
#pragma endregion



#pragma region ITERATORS
	//
	//Box2D
	//M013: Start Iterator
	const int PhysicsMgr::begin_Box2D() const { return ZERO; }

	//M014: End Iterator
	const int PhysicsMgr::end_Box2D() const { return (int)box2D.size(); }

	//M015: Size of Shapes List
	const int PhysicsMgr::size_Box2D() const { return (int)box2D.size(); }


	//////////////////////////////////////////////////////////////////////////
	//Box3D
	
	//M016: Start Iterator
	const int PhysicsMgr::begin_Box3D() const { return ZERO; }

	//M017: End Iterator
	const int PhysicsMgr::end_Box3D() const { return (int)box3D.size(); }

	//M018: Size of Shapes List
	const int PhysicsMgr::size_Box3D() const { return (int)box3D.size(); }


	//////////////////////////////////////////////////////////////////////////
	//Circle2D
	//M019: Start Iterator
	const int PhysicsMgr::begin_Circle2D() const { return ZERO; }	

	//M020: End Iterator
	const int PhysicsMgr::end_Circle2D() const { return (int)circle2D.size(); }	

	//M021: Size of Shapes List
	const int PhysicsMgr::size_Circle2D() const { return (int)circle2D.size(); }				

	//////////////////////////////////////////////////////////////////////////
	//Circle3D
	//M022: Start Iterator
	const int PhysicsMgr::begin_Circle3D() const { return ZERO; }	

	//M023: End Iterator
	const int PhysicsMgr::end_Circle3D() const { return (int)circle3D.size(); }	

	//M024: Size of Shapes List
	const int PhysicsMgr::size_Circle3D() const { return (int)circle3D.size(); }	
#pragma endregion


#pragma region GETTERs
	//
	Box2D PhysicsMgr::getBox2D( int index ) const
	{
		int cc = 0;

		if ( (int)box2D.size() > ZERO && index < (int)box2D.size() )
		{
			list<Box2D>::iterator i;

			for ( i = box2D.begin(); i != box2D.end(); ++i )
			{
				if ( cc == index ) { return (*i);  break;
				} else { ++cc; }
			}
		}
	}

	Box3D PhysicsMgr::getBox3D( int index ) const
	{
		int cc = 0;

		if ( (int)box3D.size() > ZERO && index < (int)box3D.size() )
		{
			list<Box3D>::const_iterator i;

			for ( i = box3D.begin(); i != box3D.end(); ++i )
			{
				if ( cc == index ) { return (*i);  break;
				} else { ++cc; }
			}
		}
	}

	Circle2D PhysicsMgr::getCircle2D( int index ) const
	{
		int cc = 0;

		if ( (int)circle2D.size() > ZERO && index < (int)circle2D.size() )
		{
			list<Circle2D>::const_iterator i;

			for ( i = circle2D.begin(); i != circle2D.end(); ++i )
			{
				if ( cc == index ) { return (*i);  break;
				} else { ++cc; }
			}
		}
	}

	Circle3D PhysicsMgr::getCircle3D( int index ) const
	{
		int cc = 0;

		if ( (int)circle3D.size() > ZERO && index < (int)circle3D.size() )
		{
			list<Circle3D>::const_iterator i;

			for ( i = circle3D.begin(); i != circle3D.end(); ++i )
			{
				if ( cc == index ) { return (*i);  break;
				} else { ++cc; }
			}
		}
	}
#pragma endregion


#pragma region SETTERs
	//
	void PhysicsMgr::setBox2D( int index, const Box2D &rhs )
	{
		int cc = 0;

		if ( (int)box2D.size() > ZERO && index < (int)box2D.size() )
		{
			list<Box2D>::iterator i;

			for ( i = box2D.begin(); i != box2D.end(); ++i )
			{
				if ( cc == index )
				{
					int temp = (*i).getID(); // temporarily save objID
					(*i) = rhs;
					(*i).setID(temp);		//assign back objID
					break;
				}
				else { ++cc; }
			}
		}
	}

	void PhysicsMgr::setBox3D( int index, const Box3D &rhs )
	{
		int cc = 0;

		if ( (int)box3D.size() > ZERO && index < (int)box3D.size() )
		{
			list<Box3D>::iterator i;

			for ( i = box3D.begin(); i != box3D.end(); ++i )
			{
				if ( cc == index )
				{
					int temp = (*i).getID(); // temporarily save objID
					(*i) = rhs;
					(*i).setID(temp);		//assign back objID
					break;
				}
				else { ++cc; }
			}
		}
	}

	void PhysicsMgr::setCircle2D( int index, const Circle2D &rhs )
	{
		int cc = 0;

		if ( (int)circle2D.size() > ZERO && index < (int)circle2D.size() )
		{
			list<Circle2D>::iterator i;

			for ( i = circle2D.begin(); i != circle2D.end(); ++i )
			{
				if ( cc == index )
				{
					int temp = (*i).getID(); // temporarily save objID
					(*i) = rhs;
					(*i).setID(temp);		//assign back objID
					break;
				}
				else { ++cc; }
			}
		}
	}

	void PhysicsMgr::setCircle3D( int index, const Circle3D &rhs )
	{
		int cc = 0;

		if ( (int)circle3D.size() > ZERO && index < (int)circle3D.size() )
		{
			list<Circle3D>::iterator i;

			for ( i = circle3D.begin(); i != circle3D.end(); ++i )
			{
				if ( cc == index )
				{
					int temp = (*i).getID(); // temporarily save objID
					(*i) = rhs;
					(*i).setID(temp);		//assign back objID
					break;
				}
				else { ++cc; }
			}
		}
	}
#pragma endregion
} /* NAMESPACE */
